feat: scaffold OpenFeature feature-flag support#263
Conversation
Adds a portable OpenFeature feature-flag scaffold (FeatureFlags.cs) with an example flag default-off, gated example path, and both-state tests, so features generated from the template land behind a flag by default. Ships OpenFeature's built-in in-memory provider (evaluates with no backend); the Microsoft.FeatureManagement OpenFeature bridge is preview-only, so the scaffold does not depend on it (the template avoids preview packages). Fixes #262 > 🤖 Generated by the Daily AI Assistant Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds an OpenFeature-based feature-flag scaffold to the template. A new Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/rename-placeholders.sh`:
- Around line 127-129: The rename workflow already repoints
src/$new_name/FeatureFlags.cs, but the rename test does not cover it. Update
scripts/rename-placeholders.test.sh to assert that FeatureFlags.cs is renamed
and its contents are repointed to the new scaffold, using the existing rename
assertions in the test suite so this path stays covered alongside the other
moved files.
In `@src/Example/FeatureFlags.cs`:
- Around line 53-58: CreateClientAsync is mutating the process-wide Api.Instance
provider on every call, so previously created clients can silently switch
providers. Update the API documentation/comments on CreateClientAsync (and the
provider parameter if needed) to warn that it should be called once at startup,
since SetProviderAsync re-registers the global default provider and affects all
clients returned by GetClient. If multiple independent providers are needed
later, direct callers toward domain/named client patterns instead of repeated
CreateClientAsync calls.
In `@tests/Example.Tests/ExampleClassTests.cs`:
- Around line 97-112: BuildExampleProvider duplicates the same example-flag
variant mapping used by FeatureFlags.CreateInMemoryProvider, so it can drift if
the variant strings change. Update this test helper to reuse the production
variant constants from FeatureFlags instead of hardcoding "on" and "off", while
keeping the same InMemoryProvider, Flag<bool>, and ExampleFeature setup.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ef3d6f65-973c-430c-a41d-631f42540319
📒 Files selected for processing (7)
.editorconfigAGENTS.mdREADME.mdscripts/rename-placeholders.shsrc/Example/Example.csprojsrc/Example/FeatureFlags.cstests/Example.Tests/ExampleClassTests.cs
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
devantler-tech/actions(auto-detected)
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
{src/Example/Example.csproj,tests/Example.Tests/Example.Tests.csproj}
📄 CodeRabbit inference engine (AGENTS.md)
Keep every
net10.0target-framework declaration updated together in the library and test project files; never leave one project on an old TFM when bumping the scaffold.
Files:
src/Example/Example.csproj
{scripts/rename-placeholders.sh,scripts/rename-placeholders.test.sh,src/Example/**/*.cs,tests/Example.Tests/**/*.cs}
📄 CodeRabbit inference engine (AGENTS.md)
Keep the onboarding rename script and its test in lockstep; if a placeholder
.csscaffold file is added or changed, update the rename script substitutions accordingly.
Files:
scripts/rename-placeholders.shsrc/Example/FeatureFlags.cstests/Example.Tests/ExampleClassTests.cs
{src/**/*.cs,tests/**/*.cs}
📄 CodeRabbit inference engine (AGENTS.md)
{src/**/*.cs,tests/**/*.cs}: New features must be introduced behind an OpenFeature flag, default-off, and code paths should evaluate flags throughIFeatureClient(via theFeatureFlagshelper) rather than a provider directly.
Short-lived release flags must be removed after rollout; long-lived ops/permission flags are the exception, and trivial/mechanical changes are exempt from flagging.
Files:
src/Example/FeatureFlags.cstests/Example.Tests/ExampleClassTests.cs
src/Example/FeatureFlags.cs
📄 CodeRabbit inference engine (AGENTS.md)
The scaffold’s OpenFeature setup should use the built-in in-memory provider from
FeatureFlags.CreateInMemoryProvider; do not depend on a real backend or preview bridge in the template.
Files:
src/Example/FeatureFlags.cs
tests/Example.Tests/**/*.cs
📄 CodeRabbit inference engine (AGENTS.md)
Feature-flagged behavior must be tested in both states (on and off), and unset/default behavior should be covered as default-off.
Files:
tests/Example.Tests/ExampleClassTests.cs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: devantler-tech/dotnet-template
Timestamp: 2026-07-06T19:49:46.993Z
Learning: Validate locally with `dotnet build` and `dotnet test` before opening a PR; warnings are treated as errors.
Learnt from: CR
Repo: devantler-tech/dotnet-template
Timestamp: 2026-07-06T19:49:46.993Z
Learning: Begin every PR, issue, or comment with `> 🤖 Generated by the Daily AI Assistant` when acting as the autonomous assistant.
🔇 Additional comments (4)
src/Example/Example.csproj (1)
14-24: LGTM!.editorconfig (1)
61-63: LGTM!AGENTS.md (1)
10-10: LGTM!Also applies to: 27-35, 62-63
README.md (1)
11-11: LGTM!
- Expose EnabledVariant/DisabledVariant as public consts and reference them from the test helper (no more hardcoded on/off drift). - Document that CreateClientAsync re-registers the process-wide provider and should be called once at startup. - Cover FeatureFlags.cs rename+repoint in rename-placeholders.test.sh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Example/FeatureFlags.cs (1)
62-67: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winGlobal provider mutation will race across parallel test classes as more flags are added.
CreateClientAsyncre-registers the process-wideApi.Instanceprovider (documented now), but the scaffold's stated convention (per AGENTS.md) is that every new flag gets its own gated path and its own test class exercisingCreateClientAsync. xUnit runs different test classes in parallel by default (only tests within the same class run sequentially), so once a second test class callsCreateClientAsyncconcurrently withExampleClassTests, the two classes' provider registrations can interleave and produce nondeterministic assertions — the documentation warns callers, but doesn't prevent this within the test suite itself.Consider either isolating all
FeatureFlags-exercising test classes into a shared[Collection]with parallelization disabled, or documenting this constraint explicitly inAGENTS.md/README.mdalongside the "add a test class per flag" guidance so downstream teams don't hit intermittent CI flakiness.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Example/FeatureFlags.cs` around lines 62 - 67, Create a fix for the process-wide provider mutation in CreateClientAsync by preventing parallel test classes from interleaving Api.Instance.SetProviderAsync calls. Either group all FeatureFlags-based test classes, including ExampleClassTests, into a shared xUnit Collection with parallelization disabled, or add explicit guidance in AGENTS.md/README.md alongside the per-flag test-class convention so downstream users know this CreateClientAsync path must not run concurrently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/Example/FeatureFlags.cs`:
- Around line 62-67: Create a fix for the process-wide provider mutation in
CreateClientAsync by preventing parallel test classes from interleaving
Api.Instance.SetProviderAsync calls. Either group all FeatureFlags-based test
classes, including ExampleClassTests, into a shared xUnit Collection with
parallelization disabled, or add explicit guidance in AGENTS.md/README.md
alongside the per-flag test-class convention so downstream users know this
CreateClientAsync path must not run concurrently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 7130ff5a-5a17-4101-925f-dc1070f00b51
📒 Files selected for processing (3)
scripts/rename-placeholders.test.shsrc/Example/FeatureFlags.cstests/Example.Tests/ExampleClassTests.cs
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
devantler-tech/actions(auto-detected)
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: Analyze (csharp)
🧰 Additional context used
📓 Path-based instructions (4)
{scripts/rename-placeholders.sh,scripts/rename-placeholders.test.sh}
📄 CodeRabbit inference engine (AGENTS.md)
Keep
scripts/rename-placeholders.shandscripts/rename-placeholders.test.shin lockstep; changes to placeholder substitutions must be reflected in the test script as well.
Files:
scripts/rename-placeholders.test.sh
src/**/*.cs
📄 CodeRabbit inference engine (AGENTS.md)
Call sites must use the
FeatureFlagshelper andIFeatureClientfor feature-flag evaluation; never call the underlying provider directly.
Files:
src/Example/FeatureFlags.cs
{src/**/*.cs,tests/**/*.cs}
📄 CodeRabbit inference engine (AGENTS.md)
New features must be gated behind a feature flag by default, and both enabled and disabled states must be covered by tests.
Files:
src/Example/FeatureFlags.cstests/Example.Tests/ExampleClassTests.cs
src/Example/FeatureFlags.cs
📄 CodeRabbit inference engine (AGENTS.md)
Use the scaffold's OpenFeature in-memory provider (
FeatureFlags.CreateInMemoryProvider) in the example; do not depend on a real provider or preview-onlyMicrosoft.FeatureManagementbridge packages in the scaffold.
Files:
src/Example/FeatureFlags.cs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: devantler-tech/dotnet-template
Timestamp: 2026-07-06T20:21:58.937Z
Learning: Run `dotnet build` and `dotnet test` locally before opening a PR; warnings are treated as errors, so the build must be clean.
Learnt from: CR
Repo: devantler-tech/dotnet-template
Timestamp: 2026-07-06T20:21:58.937Z
Learning: Short-lived release flags must be removed after rollout; only long-lived ops/permission flags are exempt, and trivial/mechanical changes do not need flagging.
🔇 Additional comments (4)
src/Example/FeatureFlags.cs (2)
24-28: LGTM! Both previously flagged concerns (duplicated variant strings, undocumented global provider mutation) are resolved here.Also applies to: 56-61
1-49: LGTM!Also applies to: 69-84
tests/Example.Tests/ExampleClassTests.cs (1)
45-116: LGTM! Test coverage satisfies the on/off/unset requirement, and the previously flagged variant-constant duplication is resolved by reusingFeatureFlags.EnabledVariant/DisabledVariant.scripts/rename-placeholders.test.sh (1)
88-88: LGTM! These assertions match the correspondingsubst "src/$new_name/FeatureFlags.cs"call inscripts/rename-placeholders.sh, keeping the two scripts in lockstep as required.Also applies to: 102-103
CreateClientAsync re-registers the process-wide OpenFeature Api.Instance
provider; xUnit parallelizes across test classes by default, so the
'one test class per flag' convention would race. Put flag-exercising
classes in a shared [Collection("FeatureFlags")] so xUnit runs them
sequentially, and document the convention in AGENTS.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addressed the Valid concern. Fixed by encoding the safe pattern in the scaffold: flag-exercising test classes now share a |
Why
Projects generated from the .NET template start with no way to land a feature behind a flag — so deploy and release are coupled and rollback means a redeploy. This is the .NET stack's slice of the portfolio-wide feature-flag-first standard.
What
Adds a portable OpenFeature scaffold to the template: an example flag default-off, a gated example path, and tests covering both states (on/off/unset). Call sites stay provider-agnostic; the scaffold ships an in-memory provider so it evaluates with no backend, swappable for flagd or another provider later.
Design note (for promotion): the issue proposed the
Microsoft.FeatureManagementOpenFeature bridge, but that package is preview-only today and the template's policy is to avoid preview dependencies — so this ships the GA in-memory provider (consistent with the Go template) and documents adopting the MS bridge once it ships GA. Redirect here if you'd prefer the preview bridge instead.Fixes #262 · Part of devantler-tech/monorepo#2059